package com.spring.nbrbt.tools.otherutil;

import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.ShardedJedisPool;
import redis.clients.jedis.exceptions.JedisConnectionException;
import redis.clients.jedis.exceptions.JedisDataException;
import redis.clients.jedis.exceptions.JedisException;
import redis.clients.util.SafeEncoder;

/**
 * Created by wojunyu on 2015/8/17.
 */
public abstract class RedisCon {
	protected Jedis jedis;// 非切片额客户端连接
	protected JedisPool jedisPool;// 非切片连接池
	protected ShardedJedis shardedJedis;// 切片额客户端连接
	protected ShardedJedisPool shardedJedisPool;// 切片连接池
	static Logger logger = Logger.getLogger(RedisCon.class.getName());

	protected abstract void initialPool();

	protected abstract void initialShardedPool();

	// -------------------------------------------Jedis----------------------------
	/**
	 * Execute with a call back action with result.
	 */
	public <T> T execute(JedisAction<T> jedisAction) throws JedisException {
		Jedis jedis = null;
		boolean broken = false;
		try {
			jedis = jedisPool.getResource();
			return jedisAction.action(jedis);
		} catch (JedisException e) {
			broken = handleJedisException(e);
			logger.error(e.getMessage());
			return null;
		} finally {
			closeResource(jedis, broken);
		}
	}

	/**
	 * Execute with a call back action without result.
	 */
	public void execute(JedisActionNoResult jedisAction) throws JedisException {
		Jedis jedis = null;
		boolean broken = false;
		try {
			jedis = jedisPool.getResource();
			jedisAction.action(jedis);
		} catch (JedisException e) {
			broken = handleJedisException(e);
			logger.error(e.getMessage());
		} finally {
			closeResource(jedis, broken);
		}
	}

	/**
	 * Handle jedisException, write log and return whether the connection is
	 * broken.
	 */
	protected boolean handleJedisException(JedisException jedisException) {
		if (jedisException instanceof JedisConnectionException) {
			logger.error("Redis connection lost.", jedisException);
		} else if (jedisException instanceof JedisDataException) {
			if ((jedisException.getMessage() != null)
					&& (jedisException.getMessage().indexOf("READONLY") != -1)) {
				logger.error("Redis connection are read-only slave.",
						jedisException);
			} else {
				return false;
			}
		} else {
			logger.error("Jedis exception happen.", jedisException);
		}
		return true;
	}

	/**
	 * Return jedis connection to the pool, call different return methods
	 * depends on the conectionBroken status.
	 */
	protected void closeResource(Jedis jedis, boolean conectionBroken) {
		try {
			if (conectionBroken) {
				jedisPool.returnBrokenResource(jedis);
			} else {
				jedisPool.returnResource(jedis);
			}
		} catch (Exception e) {
			logger.error(
					"return back jedis failed, will fore close the jedis.", e);
			destroyJedis(jedis);
		}
	}

	/**
	 * 在Pool以外强行销毁Jedis.
	 */
	private void destroyJedis(Jedis jedis) {
		if ((jedis != null) && jedis.isConnected()) {
			try {
				try {
					jedis.quit();
				} catch (Exception e) {
				}
				jedis.disconnect();
			} catch (Exception e) {
			}
		}
	}

	public interface JedisAction<T> {
		T action(Jedis jedis);
	}

	public interface JedisActionNoResult {
		void action(Jedis jedis);
	}

	/**
	 * 设置过期时间
	 * 
	 * @param key
	 * @param seconds
	 * @author ruan 2013-4-11
	 */

	public void expire(final String key, final int seconds) {
		execute(new JedisActionNoResult() {
			@Override
			public void action(Jedis jedis) {
				jedis.expire(key, seconds);
			}
		});
		
//		if (seconds <= 0) {
//			return;
//		}
//		jedis.expire(key, seconds);
	}
	
	/**
	 * 删除keys对应的记录,可以是多个key
	 * 
	 * @param keys
	 * @return
	 */
	public long del(final String... keys) {
//		long count = jedis.del(keys);
//		return count;
		
		return execute(new JedisAction<Long>() {
			@Override
			public Long action(Jedis jedis) {
				return jedis.del(keys);
			}
		});
	}


	/**
	 * * 判断key是否存在 * * @param String * key * @return boolean *
	 */
	public boolean exists(final String key) {
//		boolean exis = jedis.exists(key);
//		return exis;
		
		return execute(new JedisAction<Boolean>() {
			@Override
			public Boolean action(Jedis jedis) {
				return jedis.exists(key);
			}
		});
	}
	
	public Boolean hexists(final String key, final String fieldName) {
		return execute(new JedisAction<Boolean>() {
			@Override
			public Boolean action(Jedis jedis) {
				return jedis.hexists(key, fieldName);
			}
		});
	}



	// ------------------------string--------------------

	/**
	 * 根据key获取记录
	 * 
	 * @param key
	 * @return 值
	 */
	public String get(final String key) {
		return execute(new JedisAction<String>() {

			@Override
			public String action(Jedis jedis) {
				return jedis.get(key);
			}
		});
//		String value = jedis.get(key);
//		return value;
	}

	/**
	 * 根据key获取记录
	 * 
	 * @param key
	 * @return 值
	 */
	public byte[] get(byte[] key) {
		byte[] value = jedis.get(key);
		return value;
	}

	/**
	 * 添加有过期时间的记录
	 * 
	 * @param key
	 * @param seconds
	 *            过期时间，以秒为单位
	 * @param value
	 * @return String 操作状态
	 */
	public String setEx(String key, int seconds, String value) {
		String str = jedis.setex(key, seconds, value);
		return str;
	}

	/**
	 * 添加有过期时间的记录
	 * 
	 * @param key
	 * @param seconds
	 *            过期时间，以秒为单位
	 * @param value
	 * @return String 操作状态
	 */
	public String setEx(final byte[] key,final  int seconds,final  byte[] value) {
		return execute(new JedisAction<String>() {
			@Override
			public String action(Jedis jedis) {
				return jedis.setex(key, seconds, value);
			}
		});
		
//		String str = jedis.setex(key, seconds, value);
//		return str;
	}


	/**
	 * 添加记录,如果记录已存在将覆盖原有的value
	 * 
	 * @param key
	 * @param value
	 * @return 状态码
	 */
	public void set(final String key, final String value) {
		//return set(SafeEncoder.encode(key), SafeEncoder.encode(value));
		execute(new JedisActionNoResult() {

			@Override
			public void action(Jedis jedis) {
				jedis.set(key, value);
			}
		});
	}



	/**
	 * 向List中追加记录
	 * 
	 * @param key
	 * @param value
	 * @return 记录总数
	 */
	public long lpush(final byte[] key, final byte[] value) {
		return execute(new JedisAction<Long>() {
			@Override
			public Long action(Jedis jedis) {
				return jedis.lpush(key, value);
			}
		});
//		long count = jedis.lpush(key, value);
//		return count;
	}
	
	/**
	 * 向List头部追加记录
	 * 
	 * @param key
	 * @param value
	 * @return 记录总数
	 */
	public long rpush(final byte[] key, final byte[] value) {
		return execute(new JedisAction<Long>() {
			@Override
			public Long action(Jedis jedis) {
				return jedis.rpush(key, value);
			}
		});
//		long count = jedis.rpush(key, value);
//		return count;
	}
	
	/**
	 * 覆盖操作,将覆盖List中指定位置的值
	 * 
	 * @param key
	 * @param index
	 *            位置
	 * @param value
	 *            值
	 * @return 状态码
	 */
	public String lset(final byte[] key,final int index,final byte[] value) {
		return execute(new JedisAction<String>() {
			@Override
			public String action(Jedis jedis) {
				return jedis.lset(key, index, value);
			}
		});
	}
	
    public long lpush(String key, String value) {
        return lpush(SafeEncoder.encode(key), SafeEncoder.encode(value));
    }
    
    public long rpush(String key, String value) {
        return rpush(SafeEncoder.encode(key), SafeEncoder.encode(value));
    }
    
    public String lset(String key,int index, String value) {
        return lset(SafeEncoder.encode(key),index, SafeEncoder.encode(value));
    }
	
	/**
     * 获取List中指定位置的值
     *
     * @param key
     * @param index 位置
     * @return 值
     * *
     */
    public String lindex(String key, int index) {
        return SafeEncoder.encode(lindex(SafeEncoder.encode(key), index));
    }
    
    /**
     * List长度
     *
     * @param key
     * @return 长度
     */
    public long llen(String key) {
        return llen(SafeEncoder.encode(key));
    }

    /**
     * List长度
     *
     * @param key
     * @return 长度
     */
    public Long llen(final byte[] key) {
    	return execute(new JedisAction<Long>() {
			@Override
			public Long action(Jedis jedis) {
				return jedis.llen(key);
			}
		});
//        long count = shardedJedis.llen(key);
//        return count;
    }

    /**
     * 获取List中指定位置的值
     *
     * @param key
     * @param index 位置
     * @return 值
     * *
     */
    public byte[] lindex(final byte[] key,final int index) {
    	
    	return execute(new JedisAction<byte[]>() {
			@Override
			public byte[] action(Jedis jedis) {
				return jedis.lindex(key, index);
			}
		});
    	
//        byte[] value = shardedJedis.lindex(key, index);
//        return value;
    }


	
	//------------------------------------新----------------
	// / Hash Actions ///
		/**
		 * If key holds a hash, retrieve the value associated to the specified
		 * field.
		 * <p>
		 * If the field is not found or the key does not exist, a special 'nil' value is returned.
		 */
		public String hget(final String key, final String fieldName) {
			return execute(new JedisAction<String>() {
				@Override
				public String action(Jedis jedis) {
					return jedis.hget(key, fieldName);
				}
			});
		}

		public List<String> hmget(final String key, final String... fieldsNames) {
			return execute(new JedisAction<List<String>>() {
				@Override
				public List<String> action(Jedis jedis) {
					return jedis.hmget(key, fieldsNames);
				}
			});
		}
		
		public Map<String, String> hgetAll(final String key) {
			return execute(new JedisAction<Map<String, String>>() {
				@Override
				public Map<String, String> action(Jedis jedis) {
					return jedis.hgetAll(key);
				}
			});
		}

		public void hset(final String key, final String fieldName, final String value) {
			execute(new JedisActionNoResult() {

				@Override
				public void action(Jedis jedis) {
					jedis.hset(key, fieldName, value);
				}
			});
		}

		public void hmset(final String key, final Map<String, String> map) {
			execute(new JedisActionNoResult() {

				@Override
				public void action(Jedis jedis) {
					jedis.hmset(key, map);
				}
			});

		}

		public Boolean hsetnx(final String key, final String fieldName, final String value) {
			return execute(new JedisAction<Boolean>() {

				@Override
				public Boolean action(Jedis jedis) {
					return jedis.hsetnx(key, fieldName, value) == 1 ? true : false;
				}
			});
		}

		public Long hincrBy(final String key, final String fieldName, final long increment) {
			return execute(new JedisAction<Long>() {
				@Override
				public Long action(Jedis jedis) {
					return jedis.hincrBy(key, fieldName, increment);
				}
			});
		}

		public Double hincrByFloat(final String key, final String fieldName, final double increment) {
			return execute(new JedisAction<Double>() {
				@Override
				public Double action(Jedis jedis) {
					return jedis.hincrByFloat(key, fieldName, increment);
				}
			});
		}

		public Long hdel(final String key, final String... fieldsNames) {
			return execute(new JedisAction<Long>() {
				@Override
				public Long action(Jedis jedis) {
					return jedis.hdel(key, fieldsNames);
				}
			});
		}
}
